<?

Files::$Config =& Core::$Modules['files']; 


class Files 
{
	static $Config;
	
	// генерация страниц для админки
	static public
    function admin_controller(&$Page)
	{
		$error=''; $result = null;
		$module_url=  $Page->action->module_url;
		switch ($Page->action->id)
		{
			case ('files'):
				
				
				// фиксируем текущий адрес как возвратный URL 
				ReturnURL::set();
				
				$Page->toolbar_id = 'files';
				
				
				
				//pr($Page->data);
				
				// break;
			
			// case ('upload'):
				if ($_FILES)
				{
					// если переданы файлы, загружаем в текущую папку
					$upload_dir = Files::$Config->files_dir
								  .'/'
								  .Filter::dirname(GET('subfolder'));
					// pr($upload_dir);
					
					Files::upload(array('upload_dir'=> $upload_dir));
					
					// TODO: сделать сообщение об ошибках при загружке файлов
					$error='';
					
					// редиректим на текущую страницу 
					//
					ReturnURL::redirect(REQUEST_URI, $error? 2:0);
					
				}
				
				// получаем список файлов для отображения
				$Page->data = Files::get_files();
				
				break;
				
			case ('newfolder'):	
					// получаем и фильтруем параметры из URL
					$subfolder	= Filter::dirname(GET('subfolder'));
					$newfolder 	= Filter::filename(GET('name'));
					
					// вычисляем полное имя новой папки
					if ($newfolder)
					$newfolder = Files::$Config->files_dir
								.($subfolder? "/$subfolder":"")
								."/$newfolder"	;
					
					// проверяем данные на корректность 
					if (!$newfolder || file_exists($newfolder))
						$error.=  "<div>Папка '".html($_GET['name'])."' 
									уже существует или имя некорректно</div>";
										
					// если нет ошибок, выполняем действие 
					if (!$error) 
					{
						// времено отключаем системную маску прав, чтобы установить права 0755 на новую папку
						$old_umask = umask(0);
						//chmod(dirname($newfolder), 0775);
						
						$result = mkdir($newfolder, 0777);
						chmod($newfolder, 0775);
						umask($old_umask); 
					}	
					
					
					/* 
					mkdir может вызывать проблемы при safe_mode=On
					
					Также в нормальном режиме возникают конфликты между ФТП-клиентом и PHP-скриптом. Если папка создана скриптом, то ФТП клиент не может менять у неё права, а при некоторых начальных правах - не может даже удалить. 
					
					Одно из решений: изменить uid (владельца) для файла самого PHP скрипта - либо через выполнение системной команды, либо через копирование,удаление-оригинала,и-переименование-в-исходное-имя-нового-файла самого скрипта.
				
Вот комментарий про эту же проблему:
---
When safe_mode is enabled, and you create a directory via mkdir, you can't create a second inside the first because the first folder's user is that one of the webserver and not that one of the script. This is dumb and very annoying :( 
---
I've been wrestling with mkdir on a server with safe mode turned on (I think, I kept getting barmy permissions on the new dirs). 

In the end the solution was to use:
exec (mkdir ...
exec (chmod ...

For this to work I had to use the setuid bit on the script itself. I tried this with the mkdir() function, but to no avail. (To do the setuid bit I ran chmod 4775 on the script file from the command line.) 
				
					
					
					*/
					
					
					
					// TODO:  разработчика сайта нужно извещать в случае ошибки "не могу создать папку" и ошибки "не могу загрузить и создать файл" (на случай, если разработчик просто забыл настроить корректные права на папки после копирования сайта по ФТП) - записывать в лог ошибок, сообщать в дебаггере, и сообщать разработчику на почту, а также проверять все папки на права-записи в скрипте тестировщике системы 
					
					if (!$result && !$error) 
					{
						$error.= $errmsg="
							<div>
								Не удалось создать папку 
								'".html($_GET['name'])."' ($newfolder)
							</div>";
					
						trigger_error($errmsg , E_USER_WARNING);
						
					}						
					
					// создаем сообщение об ошибке
					if ($error) $Page->content= "Ошибка!<br>".$error;
					
					// редиректим туда, откуда пришли
					// pr(ReturnURL::$url);
					
					ReturnURL::redirect($module_url, $error? 2:0);
					
					
				break;
			case ('rename'):	
					// получаем и фильтруем параметры из URL
					$folder	= Filter::dirname($_GET['subfolder']);
					$file1 	= Filter::filename($_GET['name1']);
					$file2 	= Filter::filename($_GET['name2']);
					
					// вычисляем корневую папку и путь к файлам
					$dir = Files::$Config->files_dir;
					if ($folder) $dir.= "/$folder";
					
					// проверяем на корректность данные и генерируем ошибки
					if ($file1 && is_dir("$dir/$file1") && $file1[0]=='_')
						$error.=  "<div>Папка '"
									.html($_GET['name1'])
									."' системная и не может быть переименована</div>";
					else 
					if ($file2 && $file2[0]=='_') $file2=ltrim($file2,'_');
					
					if (!$file1 || !file_exists("$dir/$file1"))
						$error.=  "<div>Исходный файл '"
									.html($_GET['name1'])
									."' не существует или имя некорректно</div>";
									
					if (!$file2 || file_exists("$dir/$file2"))
						$error.= "<div>Новое имя файла '"
									.html($_GET['name2'])
									."' некорректно или уже используется</div>";
					
					
					// если нет ошибок, осуществляем действие: переименование
					if (!$error) $result = rename("$dir/$file1", "$dir/$file2");
					if (!$result) $error.= "<div>Не удалось переименовать файл 
											'".html($_GET['name1'])."'</div>";
					
					
					// создаем сообщение о выполнении действия или ошибке
					$Page->content= $error?  "Ошибка!<br>".$error 
											: "Переименование...";
					
					
					// редиректим туда, откуда пришли
					
					ReturnURL::redirect($module_url, $error? 2:0);
				
				break;
		
			case ('delete'):
					// получаем и фильтруем параметры из URL
					$folder	= Filter::dirname($_GET['subfolder']);
					$file 	= Filter::filename($_GET['name']);
					
					// вычисляем корневую папку и путь к файлам
					$dir = Files::$Config->files_dir;
					if ($folder) $dir.= "/$folder";
					
					// проверяем на корректность данные и генерируем ошибки
					if (!$file || !file_exists("$dir/$file"))
						$error.=  "<div>Исходный файл '".html($_GET['name'])."' 
										не существует или имя некорректно</div>";
					
					// если нет ошибок, осуществляем действие: удаление
					if (!$error) $result = Files::delete("$dir/$file");
					if (!$result) $error.= "<div>Не удалось удалить файл 
											'".html($_GET['name'])."'</div>";
											
					// создаем сообщение о выполнении действия или ошибке
					$Page->content= $error?  "Ошибка!<br>".$error 
											: "Удаление...";
					$time = $error? 2:0;
					
					// редиректим туда, откуда пришли
					ReturnURL::redirect($module_url, $error? 2:0);
					
				break;	
				
				
			case ('settings'):
				Admin::action_settings(self::$Config->id);
				
				break;	
			
		}
	}
	
	
	/**
	 * Delete a file or recursively delete a directory
	 *
	 * @param string $str Path to file or directory
	 */
	static function delete($str)
	{
		if(is_file($str)) return @unlink($str);
		if (is_dir($str)) 
		{
			$scan = glob(rtrim($str,'/').'/'.'*');
				foreach($scan as $path)
					self::delete($path);
			return @rmdir($str);
		}	
	}

	
	// возвращает все полезные свойства файла
	static function get_file_info($file)	
	{
		
		$file_info = array(); $imgsize=null;
		$icons32url = Files::$Config->templ_url .'/images/fileicons32';
		$icons16url = Files::$Config->templ_url .'/images/fileicons16';
		
		
		if( 0!==strpos($file,SITE_ROOT)) $file= SITE_ROOT . $file;
		
		if ($file && file_exists($file))
		$file_info = array(
				'path'		=> $file,
				'dir'		=> $dir=dirname($file),
				'url' 		=> path2url($file),
				'url_dir'	=> path2url($dir),
				'name'		=> $name = basename($file),
				
				'is_file'	=> $is_file 	= is_file($file),
				'is_folder'	=> $is_folder 	= is_dir($file),
				'is_dir'	=> $is_folder,
				'is_image'	=> $is_image 	= is_image($file),
				
				'ext'		=> $ext = ($is_file? file_ext($file) : 'folder'),
				'size'		=> $size= $is_file? filesize($file) 
										: count(scandir($file))-2, 
				'formatsize'=> $is_file? format_bytes($size,1) : $size.' files', 
				'modify'	=> date("d.m.y H:i", filemtime($file)),
				
				'width'		=> ($is_image && $imgsize=getimagesize($file) ) ? 
									$imgsize[0] : null ,
				'height'		=> @$imgsize[1],
				'img_formatsize'=> $is_image? @$imgsize[0] .'x'. @$imgsize[1].' px' : null,
				'tag_img_size' 	=> @$imgsize[3],
				'icon32'	=>  find_file_exists($icons32url."/$ext.gif" , $icons32url."/default.gif"),
				'icon16'	=> find_file_exists($icons16url."/$ext.gif" , $icons16url."/default.gif"),
			);
			
			// if($file_info['is_dir']) pr($file_info);
			
			return (object)$file_info;
	}
	
	
	// получает список файлов из папки, заданой в URL
	static function get_files()	
	{
	
		$dir =  Files::$Config->files_dir; 
		//pr($dir);
			// считываем просматриваемую директорию из URL  
			$subfolder = Filter::dirname(GET('subfolder'));
			if ($subfolder && is_dir($newdir = $dir.'/'.$subfolder))
				$dir = $newdir;
	 		
		// $filter = 	@$_GET['filter_name'] ? '*'. $_GET['filter_name'].'*' : '*';
		$filter = '*'. trim(val($_GET['filter_name'])).'*';
		$filter = $dir.'/'.$filter;
		// pr($filter);
		
		$fileslist = glob($filter); 
		// pr($filter);
		// pr($fileslist);
		
		$files=$dirs=array();
		foreach ($fileslist as $file)
		{
			// pr($file);
			$file 		= Files::get_file_info($file);
			// pr($file);
			
			$file->url 	= $file->is_file? $file->url :
						  url_merge("pg=&subfolder={$subfolder}/{$file->name}");
			$file->subfolder = $subfolder;
			
			if ($file->is_dir)  $dirs[]=$file;
			else $files[]=$file;
		}
		// pr($files);
		// pr($dirs);
		
		$result = array_merge($dirs,$files);
		
		Core::$Page->pagination = $p =
				(object)array(
					'per_page' 	=> $per_page = (int)Files::$Config->files_per_page,
					'total' 	=> count($result),
					'total_files' 	=> count($files),
					'total_dirs' 	=> count($dirs),
					'pg'		=> $pg = max(1, (int)GET('pg')),
					'start'  	=> $start = $per_page*($pg-1),
				);	
				//pr($p);
				
		$result = array_slice($result, $start, $per_page );
		
		
		
		
	
		return $result;
	} 
	
	
	// возвращает информацию о загруженном файле
	static function upload_info($key1, $key2='', $key3='', $key4='')
	{
		// pr(func_get_args());
		//echo "$key1, $key2, $key3, $key4 | ";
		
		$suffix ="";
		
		// if($key2 || $key2===0)
		if ($key2!=='') $suffix.= "['$key2']";
		if ($key3!=='') $suffix.= "['$key3']";
		if ($key4!=='') $suffix.= "['$key4']";
		//pr($suffix);
		
		$info =& $_FILES[$key1]; 
		//pr($info);
		if (!isset($info['error'])
			OR !isset($info['name'])
			OR !isset($info['type'])
			OR !isset($info['tmp_name'])
			OR !isset($info['size'])
			) 
				return false;
		// pr(79);
		
		eval ($code='
				$name	 = $_FILES[$key1]["name"]'.$suffix.';
				$type	 = $_FILES[$key1]["type"]'.$suffix.';
				$size	 = $_FILES[$key1]["size"]'.$suffix.';
				$tmp_name= $_FILES[$key1]["tmp_name"]'.$suffix.';
				$error	 = $_FILES[$key1]["error"]'.$suffix.';
				
			 ');
		//pr($code);
		
		if (!$size && !$name ) return false;
			
		$result=(object)array(
				'name'		=> $name,
				'ext'		=> file_ext($name),
				'type'		=> $type,
				'size'		=> $size,
				'error'		=> $error,
				'orig_name' => $name,
				'tmp_name'	=> $tmp_name,
				'fieldname' => $key1.str_replace ("'", "" , $suffix),
				'arraypath' => "['"
								.str_replace(
									array('^UploadFile_','^'),
									'', 
									'^'.$key1) 
								."']" .$suffix,
				);
		
		// pr($result);
		return $result;		
	}
	
	
	
	// Files::$files - Массив загруженных файлов. Индексы массива - имена полей, как они написаны в форме. Значения массива - стандартные-объекты со свойствами файла и вспомогательными атрибутами.
	static $files = array();
	
	
	
	/*** 	
	
	Files::upload($config=array()) 
	обрабатывает все файлы, загруженные из POST-формы, записывает в необходимую директорию и помещает пути файлов в соответствующие перменные массива $_POST 
		
	Параметры для загрузки файлов берутся из конфига  модуля Files (файл module_config.php, секция 'upload_config'), затем перекрываются соответствующими  параметрами из конфига текущего активного модуля (файл module_config.php, секция 'upload_config') и затем перекрываются соответсвтующими параметрами из аргумента функции $config. 
		
	В ассоциативном массиве $config можено использовать следующие параметры для загрузки файлов:
		upload_dir		- директория, куда загружать файлы (по умолчанию это FILES_DIR.'/upload')
		
		allowed_ext		- массив с разрешенными расширениями файлов
		forbidden_ext	- массив с запрещенными расширениями файлов
		max_size		- максимальный вес файла в байтах
		max_width		- максимальная ширина картинки (большие картинки уменьшаются до этого размера)
		max_height		максимальная высота картинки (большие картинки уменьшаются до этого размера)
		
		result_filepath		- если установлено (не пусто), то в соответствующий элемент массива $_POST кладется полный путь к файлу (по умолчанию $_POST кладется url к файлу) 
		
		
	Пример: 
		Files::upload();
		
		Files::upload(array('upload_dir'=> FILES_DIR.'/upload'));
		
		Files::upload(array('upload_dir'=> FILES_DIR."/$subfolder", 'allowed_ext'=> array('jpg', 'gif', 'png') ));
		
		
		
	*/
	static function upload($config=array())
	{
		// pr($_FILES);
		// pr($_POST, $_SERVER);
	
	/* Проверка на правильное оформление формы*/
	if (empty($_FILES) && !empty($_POST['UploadFile_data']))
		trigger_error('Файлы не могут быть загружены, так форма не содержит  enctype="multipart/form-data"  ');
	
	
	/* Убеждаемся в том, что файлы действительно загружались через форму */
		if ($_SERVER['REQUEST_METHOD'] != 'POST' 
			|| empty($_FILES) || !is_array($_FILES) 
			) 
			return; 
		//pr(1);
		
		
		
	/* Инициализируем настройки для загрузки файлов */
		
		// получаем базовые настройки для загрузки файлов
		$UploadConfig = (object) Files::$Config->upload_config;
		
		// прибавляем (записываем поверх) настройки текущего модуля 
		
		$module_config = Core::$Modules[Core::$Request->module];
		$module_config = val($module_config->upload_config);
		
		if ($module_config && is_array($module_config))
			$UploadConfig = (object)array_merge((array)$UploadConfig, $module_config);
		
		// прибавляем настройки, переданные в функцию
		if ($config && is_array($config))
			$UploadConfig = (object)array_merge((array)$UploadConfig, $config);
		
		
		// pr($UploadConfig);
	
		// проверяем, можно ли писать файлы в целевую директорию, создаем директорию
		$upload_dir = $UploadConfig->upload_dir;
		if (!file_exists($upload_dir))  
		{
			mkdir_recursive($upload_dir,0777);
			@chmod($upload_dir,0777);
		}
		
		// Если не получается создать директорию или закрыта от записи - выдаем ошибку 
		if (!is_writable($upload_dir)) 
			return trigger_error("WARNING: Upload dir '$upload_dir' no exists or is not writable! ", E_USER_WARNING );
		
		
	
	/* Собираем информацию о загруженных файлах из массива $_FILES */
		// обрабатываем массив $_FILES на 3 уровня вглубь, 
		// до имен полей, вроде 'data[img][]'  
		// (при необходимости легко можно расширить до 4-5 уровней вглубь)
		
		
		$files =& Files::$files; 
		foreach($_FILES as $key1=>$info)
		{
			// pr($key1,$info);
			
			if (!is_array($info['name'])) 
				$files[$key1]= Files::upload_info($key1);
			else 
			foreach($info['name'] as $key2=>$filename)
			{
				// pr($key2,$filename);
				if(!is_array($filename))
					$files["{$key1}[{$key2}]"] = 
						Files::upload_info($key1, $key2);
				else
				foreach($info['name'][$key1] as $key3=>$filename)
				{
					if(!is_array($filename))
						$files["{$key1}[{$key2}][{$key3}]"] = 
							Files::upload_info($key1, $key2, $key3);
				}
			}				
		}
		
		// pr($files);
		
	/** Обрабатываем загруженые файлы **/
		foreach ($files AS $key => &$file) 
		{
			
			
			if (!$file) {unset($files[$key]); continue;}
	
	
	/* Проверяем все загруженные файлы на соответствие ограничениям */		
			$file->ok=true; 
			$file->error_msg='';
			//pr($file);
			
			if ($file->error !=UPLOAD_ERR_OK)
				$file->error_msg='Upload error';
			
			elseif (!is_uploaded_file($file->tmp_name))
				$file->error_msg='Incorrect file';
			
			elseif ($UploadConfig->max_size 
					&& $file->size > $UploadConfig->max_size)
				$file->error_msg='Too big file';
			
			elseif ($UploadConfig->allowed_ext
					&& !in_array($file->ext, $UploadConfig->allowed_ext) )
				$file->error_msg='Incorrect file type';
			
			elseif ($UploadConfig->forbidden_ext
					&& in_array($file->ext, $UploadConfig->forbidden_ext) )
				$file->error_msg ='Incorrect file type';
			
			// определение ошибки - слишком большой  файл 	
			//if(isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH']>2097152) $error = 'Upload FAILED, file is too large !';
			
			//pr($files);
			// если есть ошибки - переходим к следующему файлу
			if ($file->error_msg) {$file->ok = false; continue;}
			
			// pr($files);
		
	/*  Вычисляем безопасное имя для файла */
		
			// делаем транслитерацию русских букв, убираем пробелы и нестадартные символы 
			$file->name = Filter::filename($file->name);
			// pr($file->name);
			
			// уменьшаем слишком длинное имя файла
			$file->basename = file_wo_ext($file->name);
			$max_filename = val($UploadConfig->max_filename, 100);
			//pr(strlen($file->name));
			
			if (strlen($file->name) > $max_filename)
			{
				
				if (strlen($file->ext) >6) 
					$file->ext = substr($file->ext,0,6);
				if (strlen($file->basename) > $max_filename-6) 
					$file->basename = substr($file->basename,0, $max_filename-6);
				// pr($file);
				
				$file->name = $file->basename .'.'. $file->ext;
			}
		
		// pr($files);
		
		// ищем свободное имя файла (добавляем циферку в конце имени, если такой файл существует)
			$i=1;
			do 	
				$file->path = $upload_dir .'/' 
							  .$file->basename 
							  .($i>1 ? ".$i" : "" )
							  . '.'.$file->ext;
			while 
				(++$i && file_exists($file->path));
			// pr($file); mkdir
			
	/* 	перемещаем файл в директорию для загрузки файлов  */
		
			$file->ok = move_uploaded_file($file->tmp_name, $file->path);
			if (!$file->ok) {continue;}
			
			$chmod = 0644;
			chmod($file->path, $chmod);
	
	/*  помещаем url-путь файла в соответсвтующее поле в массиве $_POST  */
		
			$file->url = path2url($file->path);
			eval ("\$_POST{$file->arraypath} = \$file->url ;");
	    
			// pr($files, $_POST);
		}
	
	}

}